// fs.h: File System

#pragma once

#include "sfs/disk.h"
#include <vector>
#include <stdint.h>
#include <string>
#include <cstring>
#include <unordered_map>
#include <map>
using namespace std;




struct Dentry {
    string name;              // 当前文件或目录名
    map<string,uint32_t> children;  // 子目录项缓存,uint32_t为inode物理地址
};

// 每块4KB
class FileSystem { // 系统调用，通过用户函数进行调用，然后进入内核态，调用驱动程序
public:
    const static uint32_t MAGIC_NUMBER	     = 0xf0f03410;
    const static uint32_t INODES_PER_BLOCK   = 128;
    const static uint32_t POINTERS_PER_INODE = 5;
    const static uint32_t POINTERS_PER_BLOCK = 1024; // 1个指针4B，一个块有4KB/4B=1024，每个指针指向一个数据块
    const static uint32_t INODE_START_ADDR = 1;  // 简化了i_map和b_map在块组中的占用，i_map和b_map直接另外开辟内存进行模拟
    const static uint8_t DEFAULT_MODE = 0x66;  // 修改为0x33: rw-rw- (用户rw,others rw)
    // TODO: Internal member variables
    string current_user;  // 当前用户
    uint32_t current_dir_ino;  // 当前目录的inode号
    string current_dir;
    vector<bool> i_map, b_map;

private:
    struct SuperBlock {		// Superblock structure,16B，可能没有进行分块系统就一个块组，所以就只有一个超级块
    	uint32_t MagicNumber;	// File system magic number
    	uint32_t Blocks;	// Number of blocks in file system
    	uint32_t InodeBlocks;	// Number of blocks reserved for inodes，同理只表示为块组中inode分配的默认物理块数量
    	uint32_t Inodes;	// Number of inodes in file system，同理表示主块组中总的inode数量
        uint32_t free_inode_count; // 空闲inode数
        uint32_t s_free_block_count; // 空闲块数
        uint32_t next_available_ino; // 下一个可用的inode号
        uint32_t next_available_blk; // 下一个可用的块号
        uint32_t DATA_START_ADDR; // 数据块开始地址

    };

    struct Inode { // 32B
    	uint32_t Valid;		// Whether or not inode is valid
    	uint32_t Size;		// Size of file
    	uint32_t Direct[POINTERS_PER_INODE]; // Direct pointers，直接指针，直接指向数据块。直接块在format中，也被memset初始化为0，即0就表示没有直接块
    	uint32_t Indirect;	// Indirect pointer，一级间接指针，指向一个数据块大小(注意数据块不是块组，是块组中的数据块)。该数据块中存放4KB/4B=1024个指针，每个指针指向一个数据块。 Indirect我们在format时为了简便也memset为0,即0就是没有一级间接块
        string uname;
        uint8_t mode;
        uint8_t  type;        // 文件类型
        Dentry dentry;

    };

    /*这里Block并不代表一个块组而是对block做了抽象，因为所有的数据都是按块进行分配，所以block既可以表示块组中的每一个字段如：超级快，inode块，数据块。也可以表示不属于块组中的数据如：Inode间接指针指向的数据块指针数组。我们实际的文件系统信息都存在磁盘中，所以block只是起一个缓冲的作用，负责写入和读出数据时缓冲。*/
    union Block { // 从这里知道文件系统只有一个块组，主块中包含超级块，inode块，数据块，块指针
    	SuperBlock  Super;			    // Superblock
    	Inode	    Inodes[INODES_PER_BLOCK];	    // Inode block
    	uint32_t    Pointers[POINTERS_PER_BLOCK];   // Pointer block，表示Inode间接指针指向的数据块指针数组
    	char	    Data[Disk::BLOCK_SIZE];	    // Data block
        Block() {memset(this, 0, sizeof(Block));}
        ~Block() {}
    };

    // TODO: Internal helper functions
    bool Init_Map(Disk *disk, Block &block);
    bool Init_user();  // 初始化root用���
    bool Init_dir(Disk *disk);  // 初始化目录结构
    int allocate_inode(Disk *disk);  // inode分配函数，使用首次适应算法
    int allocate_block(Disk *disk);  // block分配函数，使用首次适应算法
    void free_blocks(Disk *disk, uint32_t *blocks, size_t count);  // 释放数据块
    void free_inode(Disk *disk, uint32_t ino);  // 释放inode
    bool remove_directory(Disk *disk, uint32_t dir_ino);  // 递归删除目录
    bool remove_file(Disk *disk, uint32_t file_ino);  // 删除文件
    ssize_t read(Disk *disk, size_t inumber, char *data, size_t length);  // 从文件开始处读取数据
    ssize_t write(Disk *disk, size_t inumber, char *data, size_t length);  // 从文件开始处写入数据


public:
    void debug(Disk *disk);
    bool format(Disk *disk);
    
    bool mount(Disk *disk);
    bool mkdir(Disk *disk, string dir_name);  // 创建目录，参数：目录名，返回值：是否创建成功
    bool cd(Disk *disk, string dir_name);
    void pwd();  // 打印当前目录的绝对路径和inode号
    void ls(Disk *disk);  // 列出当前目录下的所有文件和目录
    bool create(Disk *disk, string file_name);  // 创建文件，参数：文件名，返回值：是否创建成功
    bool cat(Disk *disk, string file_path);  // 打印文件内容
    bool remove(Disk *disk, string path);  // 删除文件或目录
    ssize_t stat(size_t inumber);
    bool vi(Disk *disk, string file_path);  // vi编辑器，参数：文件路径，返回值：是否编辑成功
    void clear();  // 清空屏幕

};
